﻿using System;

namespace Beaker
{
	/// <summary>
	/// Extension to maybe for 'nice' linq like access in linq queries.
	/// </summary>
	public static class MaybeExtensions
	{
		/// <summary>
		/// If the source maybe has a value, execute the map to get the next value, when not, return a maybe without a value.
		/// </summary>
		/// <typeparam name="TSource">The type of the value in the source maybe.</typeparam>
		/// <typeparam name="TResult">The type of the value in the resulting maybe.</typeparam>
		/// <param name="source">The source maybe.</param>
		/// <param name="map">The mapping to get the result value from the source mabye.</param>
		/// <returns>A maybe with the result, or an empty maybe.</returns>
		public static Maybe<TResult> Select<TSource, TResult>(this Maybe<TSource> source, Func<TSource, TResult> map)
		{
			if (!source.HasValue || source.Value == null)
				return new Maybe<TResult>();

			return map(source.Value);
		}

		/// <summary>
		/// If the source maybe has a value, execute the map to get the next value, when not, return the default value.
		/// </summary>
		/// <typeparam name="TSource">The type of the value in the source maybe.</typeparam>
		/// <typeparam name="TResult">The type of the result.</typeparam>
		/// <param name="source">The source maybe.</param>
		/// <param name="map">The mapping to get the result value from the source mabye.</param>
		/// <param name="defaultValue">The default value to return when the maybe has no value to map.</param>
		/// <returns>The mapped value, or when the source maybe had no value, the default value.</returns>
		public static TResult Select<TSource, TResult>(this Maybe<TSource> source, Func<TSource, TResult> map, TResult defaultValue)
		{
			if (!source.HasValue || source.Value == null)
				return defaultValue;

			TResult result = map(source.Value);

			if (result == null)
				return defaultValue;

			return result;
		}

		/// <summary>
		/// If the source maybe has a value, execute the map to get the next value, when not, return the default value.
		/// </summary>
		/// <typeparam name="TSource">The type of the value in the source maybe.</typeparam>
		/// <typeparam name="TResult">The type of the result.</typeparam>
		/// <param name="source">The source maybe.</param>
		/// <param name="map">The mapping to get the result value from the source mabye.</param>
		/// <param name="defaultValue">The default value to return when the maybe has no value to map.</param>
		/// <returns>The mapped value, or when the source maybe had no value, the default value.</returns>
		public static TResult Select<TSource, TResult>(this Maybe<TSource> source, Func<TSource, Maybe<TResult>> map, TResult defaultValue)
		{
			if (!source.HasValue || source.Value == null)
				return defaultValue;

			Maybe<TResult> result = map(source.Value);

			return result.GetValueOrDefault(defaultValue);
		}

		/// <summary>
		/// If the maybe has a value, execute the action on the value; otherwise do nothing.
		/// </summary>
		/// <typeparam name="TValue">The type of the possible value of the maybe.</typeparam>
		/// <param name="source">The source maybe.</param>
		/// <param name="actionToExecute">The action to execute if the maybe has a value.</param>
		public static void Do<TValue>(this Maybe<TValue> source, Action<TValue> actionToExecute)
		{
			if (source.HasValue)
				actionToExecute(source.Value);
		}

	}
}
